﻿using System;
using System.IO;
using System.Net;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using Net.Share;
using Net.System;
using Net.Helper;
using WebSocketSharp.Server;
using WebSocketSharp;

namespace Net.Server
{
    /// <summary>
    /// web网络服务器 2020.8.25 七夕
    /// 通过JavaScript脚本, httml网页进行连接. 和 WebClient连接
    /// 客户端发送的数据请求请看Net.Share.MessageModel类定义
    /// <para>Player:当有客户端连接服务器就会创建一个Player对象出来, Player对象和XXXClient是对等端, 每当有数据处理都会通知Player对象. </para>
    /// <para>Scene:你可以定义自己的场景类型, 比如帧同步场景处理, mmorpg场景什么处理, 可以重写Scene的Update等等方法实现每个场景的更新和处理. </para>
    /// </summary>
    public class WebServer<Player, Scene> : ServerBase<Player, Scene> where Player : WebPlayer, new() where Scene : SceneBase<Player>, new()
    {
        /// <summary>
        /// webSocket服务器套接字
        /// </summary>
        public new WebSocketServer Server { get; protected set; }
        /// <summary>
        /// websocket连接策略, 有wss和ws
        /// </summary>
        public string Scheme { get; set; } = "ws";
        /// <summary>
        /// 证书
        /// </summary>
        public X509Certificate2 Certificate { get; set; }
        /// <summary>
        /// Ssl类型
        /// </summary>
        public SslProtocols SslProtocols { get; set; }

        internal class WebServerBehavior : WebSocketBehavior
        {
            internal WebServer<Player, Scene> Server;
            internal Player client;

            protected override void OnMessage(MessageEventArgs e)
            {
                var buffer = e.RawData;
                var count = buffer.Length;
                Server.trafficStatistics.ReceivedBytesPerSecond += count;
                Server.trafficStatistics.ReceivedPacketsPerSecond++;
                if (client == null)
                {
                    var segment = BufferPool.NewSegment(buffer, 0, count, false);
                    client = Server.CheckReconnect(WebSocket.Client, segment, WebSocket);
                    return;
                }
                if (count <= client.Frame) //这个是WebsocketSharp的bug，第一次会调用两次，不知道为什么!
                    return;
                if (e.IsBinary)
                {
                    var segment = BufferPool.NewSegment(buffer, 0, count, false);
                    client.ReceiveList.Enqueue(segment);
                }
                else if (e.IsText)
                {
                    client.ReceiveHandler(e.Data);
                }
            }
        }

        protected override void CreateServerSocket(int port)
        {
            Server = new WebSocketServer($"{Scheme}://{NetPort.GetIP()}:{port}");
            if (Scheme == "wss")
            {
                if (Certificate == null)
                    Certificate = CertificateHelper.GetDefaultCertificate();
                Server.SslConfiguration.ServerCertificate = Certificate;
                Server.SslConfiguration.EnabledSslProtocols = SslProtocols;
            }
            Server.AddWebSocketService<WebServerBehavior>("/", client => client.Server = this);
            Server.Start();
        }

        protected override void AcceptHander(Player client, params object[] args)
        {
            client.WSClient = args[0] as WebSocket; //在这里赋值才不会在多线程并行状态下报null问题
        }

        /// <inheritdoc/>
        public override void Close()
        {
            base.Close();
            Server.Stop();
        }
    }

    /// <summary>
    /// 默认web服务器，当不需要处理Player对象和Scene对象时可使用
    /// </summary>
    public class WebServer : WebServer<WebPlayer, SceneBase<WebPlayer>>
    {
    }
}